#include "StdAfx.h"
#include "CrossNet.h"
#include "ImageLab.h"

//constructor
CCrossNet::CCrossNet(int r_net1, int c_net1, int d_net1, int r_net2, int c_net2, int d_net2, int tCycle)
{
	net1_r = r_net1;
	net1_c = c_net1;
	net1_d = d_net1;
	net2_r = r_net2;
	net2_c = c_net2;
	net2_d = d_net2;
	trainCycle = tCycle;
	net_som1 = new CSOM(net1_r, net1_c, net1_d, trainCycle);
	net_som2 = new CSOM(net2_r, net2_c, net2_d, trainCycle);
	net_hebbian = new CHebbian(net1_r*net1_c, net2_r*net2_c);
	net_som1->MoveImage(4);
	net_som2->MoveImage(5);
	net_trained = 0;
	net_destroyed = 1;
}

//training the network
void CCrossNet::NetTraining(double** input1, double** input2, int num_input)
{
	//validate if the cross modal is load from matrices or not
	if(net_loaded == 1)
	{
		::AfxMessageBox(CString("Data are loaded from matrices, can't be trained"), MB_OK, 0);
		return;
	}
	inputData1 = input1;
	inputData2 = input2;
	double* w_hebbian1 = (double*)calloc(num_input, sizeof(double));
	double* w_hebbian2 = (double*)calloc(num_input, sizeof(double));
	double* curwinner1 = (double*)malloc(sizeof(double)*net1_d);
	double* curwinner2 = (double*)malloc(sizeof(double)*net2_d);
	for(int i=0; i<trainCycle; i++)
	{
		net_som1->somTraining(input1, num_input);
		net_som2->somTraining(input2, num_input);
		double** weight1 = net_som1->getTrainingWinners();
		double** weight2 = net_som2->getTrainingWinners();
		int* winner1 = net_som1->getTrainingResults();
		int* winner2 = net_som2->getTrainingResults();
		//calculate the distance between the input and the winner 
		for(int k=0; k<num_input; k++)
		{
			for(int j=0; j<net1_d; j++)
			{
				w_hebbian1[k] += pow(weight1[j][k]-input1[j][k], (double)2);
			}
			for(int j=0; j<net2_d; j++)
			{
				w_hebbian2[k] += pow(weight2[j][k]-input2[j][k], (double)2);
			}
			w_hebbian1[k] = sqrt(w_hebbian1[k]);
			w_hebbian2[k] = sqrt(w_hebbian2[k]);
		}
		//train the hebbian network
		net_hebbian->NetTraining(w_hebbian1, w_hebbian2, winner1, winner2, num_input);
		for(int i=0; i<num_input; i++)
		{
			w_hebbian1[i] = 0;
			w_hebbian2[i] = 0;
		}
		net_hebbian->NetRescale();
	}
	net_trained = 1;
	net_hebbian->NetAssociated();
	somLattice1 = net_som1->getLattice();
	somLattice2 = net_som2->getLattice();
	return;
}

//get the associated network1 node from network 2
int CCrossNet::NetGet1(double* input_net2)
{
	if(net_trained!=1)
	{
		::AfxMessageBox(CString("network not trained yet"), MB_OK, 0);
		return -1;
	}
	int net2_node = net_som2->getTestingResults(input_net2);
	int result = net_hebbian->NetFindInput(net2_node);
	return result;
}

//get the associated network2 node from network1
int CCrossNet::NetGet2(double* input_net1)
{
	if(net_trained!=1)
	{
		::AfxMessageBox(CString("network not trained yet"), MB_OK, 0);
		return -1;
	}
	int net1_node = net_som1->getTestingResults(input_net1);
	int result = net_hebbian->NetFindOutput(net1_node);
	return result;
}

//get the some1 testing result
int CCrossNet::NetGetSom1(double* input_net1)
{
	if(net_trained!=1)
	{
		::AfxMessageBox(CString("network not trained yet"), MB_OK, 0);
		return -1;
	}
	int result = net_som1->getTestingResults(input_net1);
	return result;
}

//get the som2 testing result
int CCrossNet::NetGetSom2(double* input_net2)
{
	if(net_trained!=1)
	{
		::AfxMessageBox(CString("network not trained yet"), MB_OK, 0);
		return -1;
	}
	int result = net_som2->getTestingResults(input_net2);
	return result;
}
//get the closest input for the output
double* CCrossNet::NetGetData1(double* input_net2)
{
//get the associated input node
	int net2_node = net_som2->getTestingResults(input_net2);
	int result = net_hebbian->NetFindInput(net2_node);
	somLattice1 = net_som1->getLattice();
//get the input that's mapped to the node
	int* mapnum = new int;
	int* mapinput = net_som1->GetMapInput(result, mapnum);
	//if no input is mapped to the node, return the node itself
	if(*mapnum == 0)
	{
//		::AfxMessageBox(CString("No minimum input found"), MB_OK, 0);
		free(mapinput);
		int result = CCrossNet::NetGet1(input_net2);
		somLattice1 = net_som1->getLattice();
		return somLattice1[result];
	}
	
//loop to find the minumum map
	double mindistance = 999999999999;
	double* inputdata = (double*)malloc(sizeof(double)*net1_d);
	double* mininputdata = (double*)malloc(sizeof(double)*net1_d);
	for(int i=0; i<*mapnum; i++)
	{
		for(int j=0; j<net1_d; j++)
		{
			inputdata[j] = inputData1[j][mapinput[i]];
		}
		double temp = bf.eDistance(inputdata, somLattice1[result], net1_d);
		if(temp<mindistance)
		{
			mindistance = temp;
			for(int j=0; j<net1_d; j++)
			{
				mininputdata[j] = inputdata[j];
			}
		}
	}
//clean the memory
	free(mapinput);
	free(inputdata);
	return mininputdata;
}

//get the closest output for the input
double* CCrossNet::NetGetData2(double* input_net1)
{
//get the associated output node
	int net1_node = net_som1->getTestingResults(input_net1);
	int result = net_hebbian->NetFindOutput(net1_node);
	somLattice2 = net_som2->getLattice();
//get the input that's mapped to the node
	int* mapnum = new int;
	int* mapoutput = net_som2->GetMapInput(result, mapnum);
	//if no output is mapped to the data, return the node itself;
	if(*mapnum == 0)
	{
//		::AfxMessageBox(CString("No minimum output found"), MB_OK, 0);
		free(mapoutput);
		int result = CCrossNet::NetGet2(input_net1);
		somLattice2 = net_som2->getLattice();
		return somLattice2[result];
	}
	
//loop to find the minimum output
	double mindistance = 999999999999;
	double* outputdata = (double*)malloc(sizeof(double)*net2_d);
	double* minoutputdata = (double*)malloc(sizeof(double)*net2_d);
	CString str(bf.douTostr(*mapnum));
	for(int i=0; i<*mapnum; i++)
	{
		for(int j=0; j<net2_d; j++)
		{
			outputdata[j] = inputData2[j][mapoutput[i]];
		}
		double temp = bf.eDistance(outputdata, somLattice2[result], net2_d);
		if(temp < mindistance)
		{
			mindistance = temp;
			for(int j=0; j<net2_d; j++)
			{
				minoutputdata[j] = outputdata[j];
			}
		}
	}
//free memory
	free(mapoutput);
	free(outputdata);
	return minoutputdata;
}

CCrossNet::~CCrossNet(void)
{
}

//destroy the network
bool CCrossNet::NetDestroy(void)
{
	net_som1->trainDestroy();
	net_som2->trainDestroy();
	net_som1->somDestroy();
	net_som2->somDestroy();
	net_hebbian->NetDestroy();
	net_destroyed = 0;
	return 1;
}

//set if illustrate the SOM
void CCrossNet::NetIllustration(bool flag)
{
	net_som1->somIllustration(flag);
	net_som2->somIllustration(flag);
	return;
}

//get the winners of the first som at the last round
int* CCrossNet::NetGetWinners1(void)
{
	return net_som1->getTrainingResults();
}

//get the winners of the second som at the last round
int* CCrossNet::NetGetWinners2(void)
{
	return net_som2->getTrainingResults();
}

//validate if the network is destroyed or not
bool CCrossNet::IsNetDestroy(void)
{
	return !net_destroyed;
}

//get the lattice of the first SOM
double** CCrossNet::NetGetSOM1Lattice(void)
{
	return somLattice1 = net_som1->getLattice();
}

//get the lattice of the second SOM
double** CCrossNet::NetGetSOM2Lattice(void)
{
	return somLattice2 = net_som2->getLattice();
}

//get the hebbian links
double** CCrossNet::NetGetHebbian(void)
{
	return net_hebbian->NetGetHebbian();
}

//get the dimension of the Cross net
void CCrossNet::NetGetDimension(int* m_SizeX1, int* m_SizeY1, int* m_SizeX2, int* m_SizeY2, int* m_DataDimension1, int* m_DataDimension2)
{
	*m_SizeX1 = net1_r;
	*m_SizeY1 = net1_c;
	*m_SizeX2 = net2_r;
	*m_SizeY2 = net2_r;
	*m_DataDimension1 = net1_d;
	*m_DataDimension2 = net2_d;
	return;
}

//load the cross modal from matrices
void CCrossNet::NetLoadFromMatrix(double** m_SOM1, double** m_SOM2, double** m_Hebbian, int m_SizeX1,
								  int m_SizeY1, int m_DataDimension1, int m_SizeX2, int m_SizeY2,
								  int m_DataDimension2)
{
	//load the first SOM
	net_som1->somLoadFromMatrix(m_SOM1, m_SizeX1, m_SizeY1, m_DataDimension1);
	//load the second SOM
	net_som2->somLoadFromMatrix(m_SOM2, m_SizeX2, m_SizeY2, m_DataDimension2);
	//load the hebbian link
	net_hebbian->NetLoadFromMatrix(m_Hebbian, m_SizeX1*m_SizeY1, m_SizeX2*m_SizeY2);
	//change the flag
	net_loaded = 1;
	return;
}